home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 426-450 / disk_429 / dr / source / dr.c next >
C/C++ Source or Header  |  1992-05-06  |  34KB  |  1,279 lines

  1. /* TO DO:
  2. Make -o intermix dirs and files instead of separating?  Not when -r though!
  3. Some way to make -x info follow -r info when both are specified?
  4. Option to choose between absolute dates and "Yesterday" etc.
  5. Lformat type option.  Option to execute that output instead of printing it.
  6. Upto/Since date options.  -K option to show disk keys.
  7. Make -P able to handle more complex bit tests.
  8. Organize .infos some way so that .info search when using chron sort is faster?
  9. Disk key sort when -o?  No, unsorted.  Option to sort alpha/cron?
  10. Have an option that means apply pattern to all depths of -r.  Like, foo/???.c
  11.     also acts like foo/#?/???.c and foo/#?/#?/???.c, etc.  Make it use
  12.     :: after the pattern (foo/#?.c::) to mean this?
  13. Make ~ in front of pattern accept all files that DO NOT fit.
  14. Handle wildcards anywhere in the path (low priority).  Handling :: may be
  15.     tough.  Allow :: only at very end.  Allow ~ in middle.
  16. Avoid repeating "unknown option" for same letter?
  17. -A means show age of file?  #ifdef'd out?
  18. Later: use two processes on same seglist:  background scans disk, sends
  19.     flist(s) to foreground which does output.
  20. Wow, in testing this ... I found that as of July 18, 1990, my hard disk
  21.     contains 1944 files in 152 directories.  A mere 20 megger!
  22. */
  23.  
  24. /* ==========================================================================
  25.  
  26. The idea here is to make Yet Another Cli Directory Command.  What's special
  27. about this one?  It's really really fast, and it doesn't show .info files! 
  28. Instead it just shows all files that have .info's associated with them by
  29. writing the name in orange instead of white when output is to the screen. 
  30. And it puts file names (and directory names also) in as many columns (up to
  31. five) as will fit comfortably in the window.  And everything is alphabetized
  32. in columns.  It will also do Amiga patterns.  It tries a pattern first as an
  33. exact literal before expanding it, in case you have a drawer named
  34. "Doesn't work?" or something.  And it does recursive descents, faster than
  35. the Fast File System.  It is intended to replace Dir, List, rls, and du. 
  36. Future versions might even replace things like foreach and SPAT/DPAT.
  37.  
  38. Dr is written for Aztec C for Amiga, by Paul Kienitz.  Public Domain.  See
  39. the files Dr.doc and FastExNext.doc for useful information.  Documentation
  40. for pureio.c is in the source.
  41. ========================================================================== */
  42.  
  43. /* some #defines which you can make different verisons with:
  44.     if SMALLSLOW is defined it uses regular ExNext instead of FastExNext.
  45.     if WEEEEK is defined it gives recent dates as "Yesterday" or
  46.         "Tuesday" instead of an absolute date.
  47.     if LEAKAGE is defined it reports all memory allocations and freeings.
  48.     if C_NOT_ASM it does not use inline assembly language instead of C
  49.         for some sorting functions (works only with Aztec).
  50.     In fastex.c, if QUEST is defined it has the ability to put up "Please
  51.         replace volume ..." and "... read/write error" system requesters
  52.         when an error occurs; otherwise it reports errors silently.
  53. */
  54.  
  55. #define FLACK ':'        /* NOT USED */
  56.  
  57. #define WIDEFAULT 77
  58.  
  59. #define HYPH 0xAD
  60.  
  61. #define CSI "\233"
  62.  
  63. #define LWID 25
  64.  
  65. #define MAXCOLS 5
  66.  
  67. #define PREPENGTH 300
  68.  
  69. #define PUREBUFSIZE 128L
  70.  
  71. #define STACKNEEDED (1500 + 300)
  72.  
  73. /* WIDEFAULT is the assumed output width when we can't measure the window.
  74.    FLACK is char used to mark icon'd files in output.  Not used these days.
  75.    HYPH is used to mark option arguments for mane (8-bit ascii soft hyphen).
  76.    CSI is the character that starts "escape sequences"; same as esc [.
  77.    LWID is how many spaces (ideally) to use for name and size in -L output.
  78.    MAXCOLS is the max number of text columns to stack listed names in.
  79.    PREPENGTH is the maximum length of pathnames labelling recursive levels.
  80.    PUREBUFSIZE is the size of the buffer for pureio.
  81.    STACKNEEDED is the amount of stack space needed to scan a directory.
  82. */
  83.  
  84.  
  85. #include <exec/io.h>
  86. #include <exec/memory.h>
  87. #include <libraries/dosextens.h>
  88. #include <string.h>
  89. #include <Paul.h>
  90. #undef put
  91.  
  92. /* Here we have a simplified version of <devices/conunit.h> which does not
  93. pull in stuff like struct Window and struct TextFont: */
  94.  
  95. struct ConUnit {
  96.     short pad[21];
  97.     short cu_XMax, cu_YMax;
  98. };
  99. /* pretty simplified, wasn't it */
  100.  
  101.  
  102.  
  103. typedef struct _fly *flip;
  104.  
  105. typedef struct _fly {
  106.     flip next;            /* list link */
  107.     long length, blox, tection;
  108.     struct DateStamp when;
  109.     str comment;
  110.     ubyte /* bool */ jected, infoed, wanted, dirred;
  111.     char name[31];
  112.     ubyte size;            /* either 70 or 80 */
  113.     short ordination;        /* consumption rounds up to 72 anyway... */
  114. } fly;
  115.  
  116. typedef struct {
  117.     struct FileInfoBlock f;
  118.     long p, s;
  119. } fib;
  120.  
  121.  
  122. struct cuont {
  123.     long blok, byt, fil, dir, jb;
  124. };
  125.  
  126.  
  127. /* Here is the stuff that would be global variables if we weren't reentrant: */
  128.  
  129. typedef struct {
  130.     adr stacklimit;
  131.     str argline;
  132.     stray argv;
  133.     int arglen, argc, hyphc;
  134.     short cwid, song, cols, wid, abort;
  135.     bool color, curse, cize, complete, cron, cons,  colsort, cutdirs, cutfils;
  136.     bool /* cage, canydepth, */ csternal, completenames, consumption;
  137.     bool zize, zomplete, /* zons, FLACK */ zurse, zutdirs, zutfils, zmptnames;
  138.     long protlook, protwant;
  139.     struct cuont tot, gran;
  140.     struct ConUnit *cuca;
  141.     struct Process *me;
  142.     long hair;
  143.     short purestuff[16];    /* pad in case changes */
  144.     int mesh[128];
  145.     str pat;
  146.     bool patty, didaninny;
  147.     char prepath[PREPENGTH];
  148.     char pat0, nullpat;
  149. } glob;
  150.  
  151. #define GG register glob *g
  152.  
  153.  
  154. /* the only global variables visible here: */
  155.  
  156. import int Enable_Abort;    /* has to stay always 1 */
  157. long goofset;            /* works like boofset in pureio */
  158.  
  159. /* there are actually others, like library bases and pureio's boofset, but
  160. they are all constants, not variables. */
  161.  
  162.  
  163. str helpslab[] = {
  164.     "      -C means sort oldest to newest, not alphabetically\n",
  165.     "      -D means show subdirectory names only\n",
  166.     "      -F means show file names only\n",
  167.     "      -H means sort in rows instead of columns\n",
  168.     "      -I means show .info files like normal files\n",
  169. /*  "      -K means show disk addresses (keys) of files and directories\n", */
  170.     "      -L means show size, protection, datestamp, and filenote\n",
  171.     "      -O means list each file on a separate line as a complete pathname\n",
  172.     "      -R means recursively show subdirectory contents\n",
  173.     "      -S means show length of each file (-L overrides)\n",
  174.     "      -U means list no names, just show total disk space consumption\n",
  175.     "      -X means show directory's info -L style instead of its contents\n",
  176.     "      -Pb or -P~b where b is one of H S P A R W E D means show files with\n",
  177.     "          the named protection bit clear (if ~ present) or set (if no ~)\n",
  178.     null
  179. };
  180.  
  181.  
  182.  
  183. /* ================== functions: ================== */
  184.  
  185. #ifdef SMALLSLOW
  186.  
  187. #define FastExamine(L, F) Examine(L, (struct FileInfoBlock *) F)
  188. #define FastExNext(L, F)  ExNext(L, (struct FileInfoBlock *) F)
  189. #define FastExCleanup(F)
  190. #define Get80(F)          Alloc(80)
  191.  
  192. #else
  193.  
  194. import long FastExamine(BPTR l, fib *f), FastExNext(BPTR l, fib *f);
  195. import void FastExCleanup(fib *f);
  196. import adr Get80(fib *f);
  197.  
  198. #endif
  199.  
  200. #ifdef LEAKAGE
  201.  
  202. import adr AllocYell(long a, long b, str c, long d);
  203. import void FreeYell(adr a, long b, str c, long d);
  204. #define AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
  205. #define FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
  206. #define _AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
  207. #define _FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
  208.  
  209. #endif
  210.  
  211. import bool CmplPat(str pat, int *aux),        /* PatMatch by Jeff Lydiat */
  212.         Match(str pat, int *aux, str S);
  213.  
  214. import bool OpenPureIO(short *b, ulong s);
  215. import void ClosePureIO(void), puch(short c), put(str s),
  216.         putfmt(str f, ...), pflush(void);
  217.  
  218.  
  219.  
  220. long StackLeft(GG)
  221. {
  222.     int i;
  223.     return (long) &i + 14 - (long) g->stacklimit;
  224. }
  225.  
  226.  
  227.  
  228. short digits(register ulong l)
  229. {
  230.     register short d = 0;
  231.     while (l) {
  232.     d++;
  233.     l /= 10;
  234.     }
  235.     if (d) return d; else return 1;
  236. }
  237.  
  238.  
  239.  
  240. void pad(short w)
  241. {
  242.     while (--w >= 0)
  243.     puch(' ');
  244. }
  245.  
  246.  
  247.  
  248. void padong(ulong n, short w)
  249. {
  250.     pad(w - digits(n));             /* putfmt doesn't have %*ld */
  251.     putfmt("%ld", n);
  252. }
  253. /* putfmt doesn't have %lu either, so let's just hope n is always positive */
  254.  
  255.  
  256.  
  257. void putection(register ulong bits)
  258. {
  259.     char bee[10];
  260.     register short b;
  261.  
  262.     bits ^= 15;
  263.     strcpy(bee, " hsparwed");
  264.     for (b = 0; b <= 7; b++)
  265.     if (!(bits & bit(b)))
  266.         bee[8 - b] = '-';
  267.     put(bee);
  268. }
  269.  
  270.  
  271.  
  272. void putdate(struct DateStamp *when)
  273. {
  274.     long day = when->ds_Days;
  275.     register short yell, mday, month, year;
  276.     short hour = (short) when->ds_Minute / 60,
  277.         minute = (short) when->ds_Minute % 60,
  278.         second = (short) when->ds_Tick / (short) TICKS_PER_SECOND;
  279.     static short smods[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  280.     short mods[12];
  281.     static char mane[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0"
  282.                 "Sep\0Oct\0Nov\0Dec";
  283. #ifdef WEEEEK
  284.     /* betcha the reason they started the calendar from the beginning of
  285.        1978 in particular is because that way day zero is a Sunday */
  286.     static str weak[7] = { "Sunday", "Monday", "Tuesday", "Wednesday",
  287.                 "Thursday", "Friday", "Saturday" };
  288.     struct DateStamp today;
  289.     short daydif;
  290.  
  291.     DateStamp(&today);
  292.     daydif = today.ds_Days - day;
  293.     if (!daydif)
  294.     put(" Today    ");
  295.     else if (daydif == 1)
  296.     put(" Yesterday");
  297.     else if (daydif > 1 && daydif < 7)
  298.     putfmt(" %-9s", weak[day % 7]);
  299.     else
  300. #endif
  301.     {
  302.     if (day <= 0 || day >= 44618)
  303.         putfmt(" ?(%ld)", day);
  304.     else {
  305.         year = 78 + ((day / 1461) << 2);
  306.         mday = day % 1461;
  307.         while (mday >= (yell = (year & 3 ? 365 : 366))) {
  308.         mday -= yell;
  309.         year++;
  310.         }
  311.         /* 2000 is a leap year?, 1900 and 2100 are <invalid> */
  312.         for (month = 0; month < 12; month++)
  313.         mods[month] = smods[month];
  314.         if (!(year & 3)) mods[1] = 29;
  315.         month = 0;
  316.         while (mday >= mods[month])
  317.         mday -= mods[month++];
  318.         putfmt(" %2d-%s-%02d", mday + 1, mane + (month << 2), year % 100);
  319.     }
  320.     }
  321.     if (hour < 0 || hour >= 24)
  322.     putfmt(" ?(%ld,%ld)", when->ds_Minute, when->ds_Tick);
  323.     else {
  324.     putfmt(" %2d:%02d:", hour, minute);
  325.     if (second < 0 || second >= 60)
  326.         put("??");
  327.     else
  328.         putfmt("%02d", second);
  329.     }
  330. }
  331.  
  332.  
  333.  
  334. /* Another personal ad found in EXPRESS "The East Bay's Free Weekly":
  335.     HI.
  336.    Yup, that was the whole ad.  Right after it:
  337.     RALPH, a 1967 Cadillac, now accepting devotees.
  338. */
  339.  
  340.  
  341.  
  342. void Lose(register flip f)
  343. {
  344.     if (f) {
  345.     if (f->comment)
  346.         FreeMem(f->comment, 80L);
  347.     FreeMem(f, (long) f->size);
  348.     }
  349. }
  350.  
  351.  
  352.  
  353. void _abort(void)  /* called by ^C checker */
  354. {            /* is this a hack, or is this a hack? */
  355.     GG = (adr) (((str) ThisProcess()->pr_ReturnAddr) + goofset);
  356.     if (g->abort) return;
  357.     g->abort = 5;
  358.     put(" *** BREAK\n");
  359. }
  360.  
  361.  
  362.  
  363. /* This is more efficient that a series of &&'s: */
  364.  
  365. #define bask(C) bit(C - 33)
  366. #define PASK (bask('?') | bask('\'') | bask('#') | bask('%') | \
  367.         bask('(') | bask(')'))
  368.  
  369. bool patchar(register ubyte cc)
  370. {
  371.     register ubyte c = cc;
  372.     return c > ' ' && (c == '|' || (c < 'A' && bit(c - 33) & PASK));
  373. }
  374.  
  375.  
  376.  
  377. /* Takes dir/pat string in from and translates it into a compiled pattern in
  378.    mesh (and uncompiled in pat) and a lock on the directory in deer to be
  379.    scanned using the pattern */
  380.  
  381. bool SplitTailPat(str from, BPTR *deer, GG)
  382. {
  383.     register str p;
  384.     register bool f = true;
  385.  
  386.     g->pat = from;
  387.     for (p = from; *p; p++)
  388.     if (*p == ':' || *p == '/')
  389.         g->pat = p + 1;        /* pat will point to path tail */
  390.     for (p = g->pat; *p; p++)
  391.     if (patchar(*p)) {f = false; break;}    /* f is for fucked */
  392.     if (f) {
  393.     putfmt("Couldn't find \"%s\".\n", from);
  394.     g->hair = ERROR_OBJECT_NOT_FOUND;
  395.     return false;
  396.     }
  397.     g->pat0 = *g->pat;        /* truncate from by temporarily damaging pat */
  398.     /* should change CmplPat to be more forgiving of unmatched parens and empty
  399.        vertical-bar halves, but I don't understand how it works yet ****/
  400.     if (!CmplPat(g->pat, g->mesh)) {
  401.     putfmt("Bogus pattern \"%s\".\n", g->pat);
  402.     return false;
  403.     }
  404.     *g->pat = 0;
  405. #ifdef UNNECESSARY
  406.     for (p = from; *p; p++)        /* truncate final slash in from */
  407.     if (*p != '/') f = true;    /*   if it contains non-slashes */
  408.     if (f && *(--p) == '/') *p = 0;
  409. #endif
  410.     if (!(*deer = RLock(from))) {
  411.     putfmt("Couldn't find directory \"%s\".\n", from);
  412.     g->hair = ERROR_OBJECT_NOT_FOUND;
  413.     return false;
  414.     }
  415.     return true;
  416. }
  417.  
  418.  
  419.  
  420. void HairSpray(register long hair)
  421. {
  422.     if (hair == ERROR_DEVICE_NOT_MOUNTED)
  423.     put("disk removed from drive.\n");
  424.     else if (hair == ERROR_NO_FREE_STORE)
  425.     put("not enough memory.\n");
  426.     else if (hair == ERROR_NOT_A_DOS_DISK)
  427.     put("disk unreadable.\n");
  428.     else if (hair == ERROR_TOO_MANY_LEVELS)
  429.     put("not enough stack space.\n");
  430.     else
  431.     putfmt("DOS error code %ld.\n", hair);
  432. }
  433.  
  434.  
  435.  
  436. flip Fly(register fib *b)
  437. {
  438.     register flip z = New(fly);
  439.     if (!z) {
  440.     z = Get80(b);
  441.     if (!z) return null;
  442.     z->size = 80;
  443.     } else z->size = sizeof(fly);
  444.     if (*b->f.fib_Comment)
  445.     z->comment = Get80(b);        /* if alloc fails, fuck it */
  446.     else z->comment = null;
  447.     if (z->comment)
  448.     strcpy(z->comment, b->f.fib_Comment);
  449.     z->next = null;
  450.     z->length = b->f.fib_Size;
  451.     z->blox = b->f.fib_NumBlocks;
  452.     z->tection = b->f.fib_Protection;
  453.     z->when = b->f.fib_Date;
  454.     strcpy(z->name, b->f.fib_FileName);
  455.     z->dirred = b->f.fib_DirEntryType > 0;
  456.     z->wanted = z->jected = z->infoed = false;
  457.     return z;
  458. }
  459.  
  460.  
  461.  
  462. flip Scan1(short *ficou, fib *deef, BPTR deer, GG)
  463. {
  464.     flip result = null;
  465.     BPTR dp, ocd, fo;
  466.     char fone[31];
  467.  
  468.     if (!(result = Fly(deef))) return null;
  469.     if (!g->curse)
  470.     g->zomplete = true;
  471.     *ficou = 1;            /* we're seeing if it has a .info file */
  472.     if (!g->cons && strlen(deef->f.fib_FileName) < 26 && (dp = ParentDir(deer))) {
  473.     ocd = CurrentDir(dp);
  474.     strcpy(fone, result->name);
  475.     strcat(fone, ".info");
  476.     Chk_Abort();
  477.     if (!g->abort && (fo = RLock(fone))) {
  478.         result->infoed = true;
  479.         UnLock(fo);
  480.     }
  481.     CurrentDir(ocd);
  482.     UnLock(dp);
  483.     }
  484.     /* WE SEEM to be running into an undocumented feature here...  the
  485.     ding bling ParentDir function sometimes sets IoErr = 212 after a
  486.     perfectly normal and SUCCESSFUL call.  When it does this, the
  487.     following RLock also does so.  So we just band-aid it: */
  488.     if (g->me->pr_Result2 == ERROR_OBJECT_NOT_FOUND
  489.             || g->me->pr_Result2 == ERROR_OBJECT_WRONG_TYPE)
  490.     g->me->pr_Result2 = 0;
  491.     return result;
  492. }
  493.  
  494.  
  495.  
  496. flip ScanInside(short *ficou, fib *deef, BPTR deer, GG)
  497. {
  498.     flip result = null, more;
  499.     long air = 0;
  500.     bool pokey;
  501.     void DoInner(str n, BPTR l, fib *f, GG);
  502.             /* vvvv prevent multiple "please replace" in -R */
  503.     while (g->hair != ERROR_DEVICE_NOT_MOUNTED && FastExNext(deer, deef)) {
  504.     Chk_Abort();
  505.     if (g->abort || !(more = Fly(deef))) break;
  506.     *g->pat = g->pat0;
  507.     if (g->zutdirs & more->dirred || g->zutfils & ~more->dirred
  508.             || (more->tection & g->protlook) != g->protwant
  509.             || !(pokey = !g->patty
  510.                 || Match(g->pat, g->mesh, more->name)))
  511.         more->jected = true;
  512.     if (pokey && g->curse && more->dirred) {
  513.         BPTR ocd = CurrentDir(deer), innerdeer;
  514.         if (innerdeer = RLock(more->name)) {
  515.         register bool p = g->patty;
  516.         g->patty = false;
  517.         *g->pat = 0;
  518.         DoInner(more->name, innerdeer, deef, g);
  519.         g->patty = p;
  520.         UnLock(innerdeer);
  521.         } else {
  522.         air = g->me->pr_Result2;
  523.         putfmt(
  524. "Can't lock inner directory \"%s\"!  Probably no memory.\n", more->name);
  525.         }
  526.         CurrentDir(ocd);
  527.     }
  528.     more->next = result;
  529.     result = more;
  530.     (*ficou)++;
  531.     }
  532.     if (air) g->me->pr_Result2 = air;
  533.     if (g->me->pr_Result2 == ERROR_NO_MORE_ENTRIES)
  534.     g->me->pr_Result2 = 0;
  535.     return result;
  536. }
  537.  
  538.  
  539.  
  540. /* scans a Dos directory and returns the findings in a linked list of fly's.
  541.    *ficou tells the number of elements in the list. */
  542.  
  543. flip ScanDeer(BPTR deer, short *ficou, fib *parent, GG)
  544. {
  545.     flip result = null, more;
  546.     register fib *deef;
  547.  
  548.     *ficou = 0;
  549.     if (!(deef = New(fib))) {
  550.     g->me->pr_Result2 = ERROR_NO_FREE_STORE;
  551.     return null;
  552.     }
  553.     if (parent)
  554.     deef->s = parent->s;
  555.     else
  556.     deef->s = g->curse ? -1 : 0;
  557.     if (FastExamine(deer, deef)) {
  558.     Chk_Abort();
  559.     if (!g->abort)
  560.         if (deef->f.fib_DirEntryType < 0 ||
  561.                 (g->csternal && !(g->patty | g->curse))) {
  562.         result = Scan1(ficou, deef, deer, g);   /* don't look inside */
  563.         if (!parent)
  564.             g->zurse = g->zutfils = g->zutdirs = false;
  565.         } else
  566.         result = ScanInside(ficou, deef, deer, g);
  567.     }
  568.     if (g->me->pr_Result2) {
  569.     g->hair = g->me->pr_Result2;
  570.     put("DR COULDN'T FINISH SCAN; ");
  571.     HairSpray(g->hair);
  572.     }
  573.     if (!parent)
  574.     FastExCleanup(deef);
  575.     Free(fib, deef);
  576.     if (g->abort)
  577.     while (result) {
  578.         more = result;
  579.         result = result->next;
  580.         Lose(more);
  581.     }
  582.     return result;
  583. }
  584.  
  585.  
  586.  
  587. #ifdef C_NOT_ASM        /* redo these in assembly for speed */
  588.  
  589. short alpha(register ubyte *a, register ubyte *b)
  590. {
  591.     register char ac, bc;
  592.     do {
  593.     ac = toupper(*(a++));
  594.     bc = toupper(*(b++));
  595.     } while (ac && ac == bc);
  596.     return ac - bc;
  597. }
  598.  
  599.  
  600.  
  601. short alpo(flip a, flip b)
  602. {
  603.     return alpha(a->name, b->name);
  604. }
  605.  
  606. #else
  607.  
  608. /* these optimized versions make a noticeable difference when sorting a
  609. directory with 75 files or more */
  610.  
  611. #asm
  612.     public        _alpha
  613.     public        _alpo
  614.  
  615. _alpo:    move.l        4(sp),a0        ; first arg
  616.     move.l        8(sp),a1        ; second arg
  617.     lea        36(a0),a0        ; first->name
  618.     lea        36(a1),a1        ; second->name
  619.     bra        alfa            ; call-and-return alpha
  620.  
  621. _alpha:    move.l        4(sp),a0        ; first arg
  622.     move.l        8(sp),a1        ; second arg
  623. alfa:    moveq        #0,d0
  624.     moveq        #0,d1
  625. nxt:      move.b    (a0)+,d0
  626.       move.b    (a1)+,d1
  627.       beq        out            ; end of string
  628.       cmp.b        d0,d1
  629.       beq        nxt            ; exactly equal chars
  630.         cmp.b    #'Z',d0            ; tolower(*a)
  631.         bgt        noupa
  632.         cmp.b    #'A',d0
  633.         blt        noupa
  634.           add.b    #'a'-'A',d0
  635. noupa:        cmp.b    #'Z',d1            ; tolower(*b)
  636.         bgt        noupb
  637.         cmp.b    #'A',d1
  638.         blt        noupb
  639.           add.b    #'a'-'A',d1
  640. noupb:        cmp.b    d0,d1
  641.         beq        nxt            ; equal after tolower
  642.  
  643. out:    sub.w        d1,d0            ; compare as unsigned bytes
  644.     rts
  645.     
  646. #endasm
  647.  
  648. import short alpha(ubyte *a, ubyte *b), alpo(flip a, flip b);
  649.  
  650. #endif
  651.  
  652.  
  653.  
  654. short olda(register flip a, register flip b)
  655. {
  656.     register long t;
  657.     if (t = a->when.ds_Days - b->when.ds_Days)
  658.     return t;
  659.     if (t = a->when.ds_Minute - b->when.ds_Minute)
  660.     return t;
  661.     return a->when.ds_Tick - b->when.ds_Tick;
  662. }
  663. /* the C code generation does perfectly well with olda */
  664.  
  665.  
  666.  
  667. #ifdef C_NOT_ASM
  668.  
  669. short infoo(ubyte *a, ubyte *b)
  670. /* returns 0 if filename b is the .info of filename a, positive if b is
  671.    alphabetically after a's .info name, negative if before. */
  672. {
  673.     char acat[37];
  674.     register short f = (*a | 0x20) - (*b | 0x20);
  675.     if (f) return f;
  676.     strcpy(acat, a);
  677.     strcat(acat, ".info");
  678.     return alpha(b, acat);
  679. }
  680.  
  681. #else
  682.  
  683. #asm
  684.     public    _infoo
  685.  
  686. _infoo:    move.l        4(sp),a1        ; first arg
  687.     move.l        8(sp),a0        ; second arg
  688. infu:    move.b        (a1),d0
  689.     move.b        (a0),d1
  690.     bset        #5,d0            ; primitive tolower()
  691.     bset        #5,d1
  692.     sub.b        d1,d0            ; quick pre-test
  693.     beq        check
  694.       ext.w        d0
  695.       rts
  696. check:    move.l        a2,-(sp)        ; the real test
  697.     link        a5,#-40
  698.     move.l        sp,a2            ; temporary copy area
  699. cpy:      move.b    (a1)+,(a2)+
  700.       bne        cpy
  701.     move.b        #'.',-1(a2)        ; overwrite final nul
  702.     move.b        #'i',(a2)+
  703.     move.b        #'n',(a2)+
  704.     move.b        #'f',(a2)+
  705.     move.b        #'o',(a2)+
  706.     clr.b        (a2)
  707.     move.l        sp,a1
  708.     bsr        alfa
  709.     unlk        a5
  710.     move.l        (sp)+,a2
  711.     rts
  712. #endasm
  713.  
  714. import short infoo(ubyte *a, ubyte *b);
  715.  
  716. #endif
  717.  
  718.  
  719.  
  720. flip Soart(flip flist, GG)
  721. {
  722.     short (*sortie)(flip a, flip b) = (g->cron ? &olda : &alpo);
  723.     bool crudecons = g->cron | g->zmptnames;
  724.  
  725.     if (!flist) return null;
  726.     if (!g->zmptnames) {
  727.     register flip *head, *dot, *foist, t;        /* last 2 get D regs */
  728.     for (head = &flist; (*head)->next; head = &(*head)->next) {
  729.         foist = head;
  730.         for (dot = &(*head)->next; *dot; dot = &(*dot)->next)
  731.         if (sortie(*foist, *dot) > 0)
  732.             foist = dot;
  733.         t = *foist;
  734.         *foist = (*foist)->next;
  735.         t->next = *head;
  736.         *head = t;
  737.     }
  738.     }
  739.     if (!g->cons) {
  740.     register flip t, tt;
  741.     register short k;
  742.     for (t = flist; t; t = t->next)
  743.         for (tt = (crudecons ? flist : t->next); tt; tt = tt->next)
  744.         if (!(k = infoo((ubyte *) t->name, (ubyte *) tt->name))) {
  745.             if (!tt->dirred) {
  746.             t->infoed = true;
  747.             if (!t->jected) tt->jected = true;
  748.             }
  749.             break;
  750.         } else
  751.             if (!crudecons && k > 0) break;
  752.     }
  753.     return flist;
  754. }
  755.  
  756.  
  757.  
  758. short CheckWindowWidth(GG)
  759. {
  760.     import long dos_packet(struct MsgPort *p, long c, ...);
  761.     struct InfoData *ind;            /* ^^^ a Manx convenience */
  762.     adr cont = g->me->pr_ConsoleTask;
  763.  
  764.     if (!g->cuca)
  765.     if (g->color && cont && (ind = NewP(struct InfoData))) {
  766.         if (dos_packet(cont, ACTION_DISK_INFO, (long) ind >> 2))
  767.         g->cuca = (adr) ((struct IOStdReq *) ind->id_InUse)->io_Unit;
  768.         else g->color = false;
  769.         Free(struct InfoData, ind);
  770.     } else g->color = false;
  771.     return g->color ? g->cuca->cu_XMax + 1 : WIDEFAULT;
  772. }
  773. /* I know, there's an escape sequence.  But it don't work without you does
  774. set_raw, which does dos_packet, and it comes out smaller this way. */
  775.  
  776.  
  777.  
  778. void Columnate(flip flist, short ficou, GG)
  779. {
  780.     short n;
  781.     register flip f;
  782. /*  bool oneinfo = false;    FLACK */
  783.     g->song = 1;
  784.     for (f = flist; f; f = f->next)
  785.     if (!f->jected) {
  786.         if ((n = digits(f->length)) > g->song)
  787.         g->song = n;
  788.     } else            /* Assumes 512-byte blocks! vvvv ****/
  789.         g->tot.jb += (f->dirred ? 1 : f->blox + f->blox / 72 + 1);
  790.     if (g->consumption) return;
  791.     g->cwid = 1;
  792.     if (!g->wid)
  793.     g->wid = CheckWindowWidth(g);
  794.     for (f = flist; f; f = f->next)
  795.     if (!f->jected) {
  796.         n = strlen(f->name) + 1 /* 2 - g->cons */ ;        /* FLACK */
  797.         if (f->dirred) n++;
  798.         else if (g->zize) n += g->song + 1;
  799.         if (n > g->cwid) g->cwid = n;
  800. /*        if (f->infoed) oneinfo = true;    FLACK */
  801.     }
  802. /*  g->zons = g->cons;
  803.     if (!(g->cons | oneinfo)) {
  804.     g->zons = true;
  805.     if (!--g->cwid) g->cwid = 1;
  806.     }                    FLACK */
  807.     if (g->zomplete) {
  808.     n = g->wid - 28;
  809.     if (g->cwid > n) g->cwid = n;    /* not enough space */
  810.     if (n > LWID) n = LWID;        /* use at least LWID if possible */
  811.     if (n > g->cwid) g->cwid = n;
  812.     } else {
  813.     g->cols = g->wid / g->cwid;
  814.     if (g->cols > MAXCOLS) g->cols = MAXCOLS;
  815. /*    if (g->cols > ficou) g->cols = ficou; */ /* on second thought, nah */
  816.     if (!g->cols) g->cols = 1;        /* window too narrow */
  817.     g->cwid = g->wid / g->cols;        /* share extra space evenly */
  818.     }
  819. }
  820.  
  821.  
  822.  
  823. void Cough1(register flip y, bool dirs, short *col, GG)
  824. {
  825.     register short h, lused = strlen(y->name);
  826.     bool icon = g->color & y->infoed;
  827.     char p;
  828.  
  829.     if (dirs) {
  830.     g->tot.blok++;
  831.     g->tot.dir++;
  832.     } else {
  833.     g->tot.byt += y->length;
  834.     g->tot.fil++;
  835.     g->tot.blok += y->blox + y->blox / 72 + 1;
  836.     }            /* this assumes  ^^^^  512-byte block size! ****/
  837.     if (g->consumption) return;
  838.     if (g->zize && !g->zomplete && !g->completenames) {
  839.     if (dirs) pad(g->song);
  840.     else padong(y->length, g->song);
  841.     puch(' ');
  842.     lused += g->song + 1;
  843.     }
  844. /*  if (!g->zons) {
  845.     puch(y->infoed ? FLACK : ' ');
  846.     lused++;
  847.     } */
  848.     if (icon) put(CSI "33m");
  849.     if (g->completenames) {
  850.     put(g->prepath);
  851.     if (*g->prepath) {
  852.         register short l = strlen(g->prepath);
  853. /* HEY, I just discovered another Aztec 3.6a compiler bug.  If you leave */
  854. /* out the line above and go "p = g->prepath[strlen(g->prepath) - 1];"   */
  855. /* where strlen is #defined as _BUILTIN_strlen, it generates code that   */
  856. /* uses the special non-existent 68000 instruction "ext.l a0".           */
  857.         p = g->prepath[l - 1];
  858.         if (p != '/' && p != ':')
  859.         puch('/');
  860.     }
  861.     }
  862.     put(y->name);
  863.     if (icon) put(CSI "31m");
  864.     if (y->dirred) {
  865.     puch('/');
  866.     lused++;
  867.     }
  868.     h = g->cwid - lused;
  869.     if (g->completenames)
  870.     puch('\n');
  871.     else if (!g->zomplete) {
  872.     if (++*col >= g->cols) {
  873.         puch('\n');
  874.         *col = 0;
  875.     } else
  876.         pad(h);
  877.     } else {
  878.     if (h > digits(y->length))
  879.         if (dirs) pad(h);
  880.         else padong(y->length, h);
  881.     else {
  882.         puch('\n');
  883.         if (dirs) pad(g->cwid);
  884.         else padong(y->length, g->cwid);
  885.     }
  886.     putection(y->tection);
  887.     putdate(&y->when);
  888.     puch('\n');
  889.     if (y->comment)
  890.         putfmt(": %s\n", y->comment);
  891.     }
  892. }
  893.  
  894.  
  895.  
  896. bool CoughHalf(flip flist, bool dirs, GG)
  897. {
  898.     short h, col = 0, n = 0;
  899.     bool anyleft = false;
  900.     register flip y;
  901.  
  902.     if (!flist) return false;
  903.     for (y = flist; y; y = y->next)
  904.     if (!y->jected)
  905.         if (!(dirs ^ y->dirred)) {
  906.         y->wanted = true;
  907.         n++;
  908.         } else {
  909.         y->wanted = false;
  910.         anyleft = true;
  911.         }
  912.     /* else wanted is always false */
  913.     if (!n) return false;
  914.     h = 0;
  915.     for (y = flist; y; y = y->next)
  916.     if (y->wanted) {
  917.         y->ordination = h;
  918.         if (g->colsort) {
  919.         if ((h += g->cols) >= n)
  920.             h = h % g->cols + 1;
  921.         } else h++;
  922.     } else y->ordination = -1;
  923.     y = flist;
  924.     for (h = 0; h < n; h++) {
  925.     while (y->ordination != h)
  926.         if (!(y = y->next)) y = flist;    /* a bit clumsy... */
  927.     Chk_Abort();
  928.     if (g->abort) break;
  929.     Cough1(y, dirs, &col, g);
  930.     }
  931.     if (col > 0) puch('\n');
  932.     return anyleft && !g->abort && !g->zutfils;
  933. }
  934.  
  935.  
  936.  
  937. void plural(str s, long n)
  938. {
  939.     putfmt(s, n, (n == 1 ? 0 : 's'));
  940. }
  941.  
  942.  
  943.  
  944. void Tote(register struct cuont *c)
  945. {
  946.     plural("%ld dir%c ", c->dir);
  947.     plural("and %ld file%c ", c->fil);
  948.     plural("with %ld byte%c ", c->byt);
  949.     plural("use %ld block%c ", c->blok);
  950.     putfmt("(of %ld).\n", c->blok + c->jb);
  951. }
  952.  
  953.  
  954.  
  955. void CoughUp(flip flist, short ficou, GG)
  956. {
  957.     flip p, t;
  958.     if (g->zurse && !g->abort && !g->zmptnames) {
  959.     if (g->didaninny && !g->consumption) puch('\n');
  960.     if (flist)
  961.         putfmt("    ------- \"%s\":\n", g->prepath);
  962.     else putfmt("    ------- \"%s\" is empty.\n", g->prepath);
  963.     g->didaninny = true;
  964.     }
  965.     if (!flist) return;
  966.     g->tot.blok = g->tot.byt = g->tot.fil = g->tot.dir = g->tot.jb = 0;
  967.     g->cols = 1;        /* default */
  968.     if (!g->zmptnames)
  969.     Columnate(flist, ficou, g);
  970. #ifdef STUPID_DASHES
  971.     if (!g->zutdirs && CoughHalf(flist, true, g)
  972.                 && !g->consumption && !g->completenames) {
  973.     short i;
  974.     put("    ");
  975.     for (i = 0; i < g->wid - 8; i++)
  976.         puch('-');
  977.     puch('\n');
  978.     }
  979. #else
  980.     if (!g->zutdirs) {
  981. #ifdef BLACKGROUND
  982.     if (g->color) put(CSI "42m" CSI "J");
  983. #endif
  984. #ifdef BOLDDIRS
  985.     if (g->color) put(CSI "1m");
  986. #endif
  987.     CoughHalf(flist, true, g);
  988. #ifdef BLACKGROUND
  989.     if (g->color) put(CSI "0m" CSI "J");
  990. #endif
  991. #ifdef BOLDDIRS
  992.     if (g->color) put(CSI "0m");
  993. #endif
  994.     }
  995. #endif
  996.     Chk_Abort();
  997.     if (!g->zutfils && !g->abort)
  998.     CoughHalf(flist, false, g);
  999.     for (p = flist; p; p = t) {
  1000.     t = p->next;
  1001.     Lose(p);
  1002.     }
  1003.     if (g->abort || g->zmptnames) return;
  1004.     if (((g->complete | g->cize) && g->tot.fil > 1) || g->consumption)
  1005.     Tote(&g->tot);
  1006.     else if ((g->zomplete | g->cize) && g->tot.fil)
  1007.     plural("%ld block%c used.\n", g->tot.blok);
  1008.     g->gran.blok += g->tot.blok;
  1009.     g->gran.byt += g->tot.byt;
  1010.     g->gran.fil += g->tot.fil;
  1011.     g->gran.dir += g->tot.dir;
  1012.     g->gran.jb += g->tot.jb;
  1013. }
  1014.  
  1015.  
  1016.  
  1017. void DoInner(str what, BPTR deer, fib *parent, GG)
  1018. {
  1019.     flip filist;
  1020.     short ficou, prength = strlen(g->prepath), preng1 = prength;
  1021.     char pe = g->prepath[prength - 1];
  1022.  
  1023.     if (!g->abort && StackLeft(g) < STACKNEEDED) {
  1024.     putfmt("Cannot scan \"%s\" -- insufficient stack space!\n", what);
  1025.     g->hair = ERROR_TOO_MANY_LEVELS;
  1026.     /* dammit, my books don't define what TOO_MANY_LEVELS means, or */
  1027.     /* when it is appropriate to set it!                            */
  1028.     return;
  1029.     }
  1030.     if (prength && pe != ':' && pe != '/') {
  1031.     g->prepath[prength++] = '/';
  1032.     g->prepath[prength] = 0;
  1033.     }
  1034.     if (prength < PREPENGTH - 30)
  1035.     strcpy(g->prepath + prength, what);
  1036.  
  1037.     g->zize = g->complete || (g->cize && !g->cutfils);
  1038.     g->zomplete = g->complete;
  1039.     g->zurse = g->curse;
  1040.     g->zutfils = g->cutfils;
  1041.     g->zutdirs = g->cutdirs;
  1042.     filist = ScanDeer(deer, &ficou, parent, g);
  1043.     if (!filist && !g->abort && (g->me->pr_Result2 == ERROR_NO_FREE_STORE)) {
  1044.     putfmt("Not enough memory to scan \"%s\"!\n", what);
  1045.     g->hair = ERROR_NO_FREE_STORE;
  1046.     }
  1047.     g->zize |= g->zomplete;
  1048.     if (!g->consumption)
  1049.     filist = Soart(filist, g);
  1050.     if (!g->abort)
  1051.     CoughUp(filist, ficou, g);
  1052.     g->prepath[preng1] = 0;    /* remove tail just added */
  1053. }
  1054.  
  1055.  
  1056.  
  1057. void Do(str what, GG)
  1058. {
  1059.     BPTR deer;
  1060.     g->pat = &g->nullpat;
  1061.     if (g->patty = !(deer = RLock(what)))
  1062.     if (!SplitTailPat(what, &deer, g))
  1063.         return;
  1064.     if (!deer) {
  1065.     putfmt("Couldn't find \"%s\".\n", what);
  1066.     g->hair = ERROR_OBJECT_NOT_FOUND;
  1067.     return;
  1068.     }
  1069.     g->gran.blok = g->gran.byt = g->gran.fil = g->gran.dir = g->gran.jb = 0;
  1070.     *g->prepath = 0;
  1071.     g->didaninny = false;
  1072.     g->zmptnames = g->completenames && !g->consumption;
  1073.     DoInner(what, deer, null, g);
  1074.     UnLock(deer);
  1075.     if (!g->abort && (g->complete || g->consumption || g->cize)
  1076.             && g->gran.blok > g->tot.blok) {
  1077.     put("\nTotal:  ");
  1078.     Tote(&g->gran);
  1079.     }
  1080. }
  1081.  
  1082.  
  1083.  
  1084. #define lower(C) ((C) | 0x20)
  1085.  
  1086. void Opt(register str p, GG)
  1087. {
  1088.     register char c;
  1089.     for (c = lower(*++p) ; c > ' '; c = lower(*++p)) {
  1090.     switch (c) {
  1091.         case 'r':  { g->curse ^= true; continue; }
  1092.         case 'i':  { g->cons ^= true; continue; }
  1093.         case 's':  { g->cize ^= true; continue; }
  1094.         case 'c':  { g->cron ^= true; continue; }
  1095.         case 'l':  { g->complete ^= true; continue; }
  1096.         case 'h':  { g->colsort ^= true; continue; }
  1097.         case 'f':  { g->cutdirs ^= true; g->cutfils = false; continue; }
  1098.         case 'd':  { g->cutfils ^= true; g->cutdirs = false; continue; }
  1099.         case 'x':  { g->csternal ^= true; continue; }
  1100.         case 'o':  { g->completenames ^= true; continue; }
  1101.         case 'u':  { g->consumption ^= true; continue; }
  1102.      /* case 'a':  { g->cage ^= true; continue; }      */
  1103.      /* case 'e':  { g->canydepth ^= true; continue; } */
  1104.         case 'p':  {
  1105.         register bool tilt = false;
  1106.         register short b = -1;
  1107.         g->protlook = g->protwant = 0;
  1108.         c = lower(p[1]);
  1109.         if (c == '~') {
  1110.             tilt = true;
  1111.             c = lower((++p)[1]);
  1112.         }
  1113.         if (c <= ' ')
  1114.             continue;
  1115.         else p++;
  1116.         switch (c) {
  1117.             case 'h': b = 7; break;
  1118.             case 's': b = 6; break;
  1119.             case 'p': b = 5; break;
  1120.             case 'a': b = 4; break;
  1121.             case 'r': b = 3; break;
  1122.             case 'w': b = 2; break;
  1123.             case 'e': b = 1; break;
  1124.             case 'd': b = 0;
  1125.         }
  1126.         if (b < 0)
  1127.             put(" *** letter after -P must be one of H S P A R W E D.\n");
  1128.         else {
  1129.             g->protlook = bit(b);
  1130.             g->protwant = tilt ^ (b >= 4) ? bit(b) : 0;
  1131.         }
  1132.         continue;
  1133.         }
  1134.     }
  1135.     putfmt(" *** Unknown option -%c.\n", *p);
  1136.     }
  1137. }
  1138.  
  1139.  
  1140.  
  1141. void mane(GG)
  1142. {
  1143.     struct FileHandle *out = gbip(g->me->pr_COS);
  1144.     short a, aa;
  1145.     bool help = g->argc == 2 && g->argv[1][0] == '?' && !g->argv[1][1];
  1146.     bool laybull = g->argc - g->hyphc > 2, didone = false;
  1147.     str *hp;
  1148.  
  1149.     if (help) {            /* [-cdfhilorsux | -pB | -p~B] */
  1150.     putfmt("\nUsage: %s { [-options] [directory|pattern] } ...\n\n"
  1151.         "where options are:\n",
  1152.         *g->argv);
  1153.     for (hp = helpslab; *hp; hp++) put(*hp);
  1154.     g->abort = 1;
  1155.     return;
  1156.     }
  1157.     g->color = IsInteractive(g->me->pr_COS) & true;        /* -1 => 1 */
  1158.     g->wid = CheckWindowWidth(g);            /* may reset g->color */
  1159.     for (aa = g->argc - 1; aa > 0 && (ubyte) *g->argv[aa] == HYPH; aa--)
  1160.     Opt(g->argv[aa], g);
  1161.     if (aa) {
  1162.     for (a = 1; a < g->argc && !g->abort; a++)
  1163.         if ((ubyte) *g->argv[a] == HYPH) {
  1164.         Opt(g->argv[a], g);
  1165.         } else {
  1166.         if (laybull && (!g->completenames || g->consumption)) {
  1167.             if (didone) puch('\n');
  1168.             didone = true;
  1169.             pad((g->wid - (short) strlen(g->argv[a]) - 10) >> 1);
  1170.             putfmt("-- %s --\n", g->argv[a]);
  1171.         }
  1172.         Do(g->argv[a], g);
  1173.         }
  1174.     } else Do("", g);
  1175.     if (g->hair && g->hair != ERROR_OBJECT_NOT_FOUND && !g->abort) {
  1176.     put("*** LISTING NOT COMPLETE; ");
  1177.     HairSpray(g->hair);
  1178.     }
  1179. }
  1180.  
  1181.  
  1182.  
  1183. /* This version of cliparse features handling of internal quotes BCPL-style
  1184.    with *" instead of Manx "".  It marks -args with a special character
  1185.    (HYPH), accepting a -arg in quotes as a filename.  *N and *E are
  1186.    not supported, since filenames can't contain newline or escape. */
  1187.  
  1188. void cliparse(GG, long alen, register str ap)
  1189. {
  1190.     register short coml;
  1191.     bool quoted, star;
  1192.     register ubyte c;
  1193.     str tempargv[200];
  1194.     register str poik = bip(char, bip(struct CommandLineInterface,
  1195.                       g->me->pr_CLI)->cli_CommandName);
  1196.  
  1197.     g->hyphc = 0;
  1198.     coml = *poik;
  1199.     g->arglen = coml + (short) alen + 2;
  1200.     if (!(g->argline = Alloc(g->arglen))) return;
  1201.     strncpy(g->argline, poik + 1, (size_t) coml);
  1202.     g->argline[coml] = '\0';
  1203.     *tempargv = g->argline;
  1204.     g->argc = 1;
  1205.     if (alen) {
  1206.     poik = g->argline + coml + 1;
  1207.     ap[--alen] = '\0';            /* probably just the newline */
  1208.     for (;;) {
  1209.         while (*ap && *ap <= ' ') ap++;
  1210.         if (!*ap) break;
  1211.         if (*ap == '-') {
  1212.         g->hyphc++;
  1213.         (ubyte) *ap = HYPH;
  1214.         }
  1215.         quoted = *ap == '"';
  1216.         star = false;
  1217.         ap += quoted;
  1218.         tempargv[g->argc++] = poik;
  1219.         while ((c = (ubyte) *ap) && (quoted ? star || c != '"' : c > ' ')) {
  1220.         if (!(star = quoted && c == '*'))
  1221.             *(poik++) = c;
  1222.         ap++;
  1223.         }
  1224.         if (*ap == '"') ap++;
  1225.         *(poik++) = '\0';
  1226.     }
  1227.     }
  1228.     tempargv[g->argc] = null;
  1229.     if (!(g->argv = Alloc((g->argc + 1) << 2))) {
  1230.     g->argc = 0;
  1231.     FreeMem(g->argline, (long) g->arglen);
  1232.     }
  1233.     for (coml = 0; coml <= g->argc; coml++)
  1234.     g->argv[coml] = tempargv[coml];
  1235. }
  1236.  
  1237.  
  1238.  
  1239. /* simplified reentrant startup code */
  1240.  
  1241. long _main(long alen, str aptr)
  1242. {
  1243.     glob g;            /* the single instance for everyone */
  1244.     adr reta;
  1245.  
  1246.     Enable_Abort = 1;
  1247.     memset(&g, 0, sizeof(glob));
  1248.     g.me = ThisProcess();
  1249.     reta = g.me->pr_ReturnAddr;
  1250.     goofset = (str) &g - (str) reta;
  1251.     if (!g.me->pr_CLI) {
  1252.     /**** maybe reply wbstartup msg here?  naah */
  1253.     return 999999L;        /* never gets unloaded by workbench */
  1254.     }
  1255.     g.stacklimit = (adr) ((str) reta - *(long *) reta + 4);
  1256.     if (OpenPureIO(&g.purestuff[0], PUREBUFSIZE)) {    /* move this to mane? */
  1257.     g.me->pr_Result2 = ERROR_NO_FREE_STORE;
  1258.     return 20L;
  1259.     }
  1260.     g.colsort = true;
  1261.     cliparse(&g, alen, aptr);
  1262.     if (g.argc)
  1263.     mane(&g);
  1264.     else {
  1265.     put("Dr:  Not enough memory to do a durn thing!\n");
  1266.     g.abort = 20;
  1267.     }
  1268.     if (g.argline) {
  1269.     FreeMem(g.argline, (long) g.arglen);
  1270.     FreeMem(g.argv, (long) (g.argc + 1) << 2);
  1271.     }
  1272.     ClosePureIO();
  1273.     if (g.hair) {
  1274.     g.me->pr_Result2 = g.hair;
  1275.     if (!g.abort) g.abort = 10;
  1276.     }
  1277.     return (long) g.abort;
  1278. }
  1279.